import 'dart:collection';
import 'dart:convert';
import 'dart:math';
import 'todo.dart';

void main() {
  /*
  Point p = Point(5, 6);
  print("p= p.x= ${p.x}, p.y= ${p.y}");


  PointX p1 = PointX(8, 9, 10);
  print("p1= p1.x= ${p1.x}, p1.y= ${p1.y}, p1.z= ${p1.z}");

  PointX p2 = PointX.origin();
  print("p2= p2.x= ${p2.x}, p2.y= ${p2.y}, p1.z= ${p2.z}");


  Employee emp = Employee.fromJson({});

  if(emp is Person){
    emp.firstName = 'Bob';
  }
  (emp as Employee).firstName = 'Bob';

  Point.fromJson(Map.of({'x': 10, 'y': 11}));

  Point.withAssert(10, 10);

  var distanceFromOrigin = PointP(2, 3).distanceFromOrigin;
  print('distanceFromOrigin= $distanceFromOrigin');
  
  Logger logger = Logger('UI');
  logger.log('Button clicked');


  print(greetBob(Person1('Kathy')));
  print(greetBob(Impostor()));

  final v = Vector(2, 3);
  final w = Vector(2, 2);
  assert(v + w == Vector(4, 5));
  assert(v - w == Vector(0, 1));


  var someBaseClassFoo = Foo<SomeBaseClass>();
  var extenderFoo = Foo<Extender>();
  var foo = Foo();
  // var fooObj = Foo<Object>();
  print('someBaseClassFoo= $someBaseClassFoo');
  print('extenderFoo= $extenderFoo');
  print('foo= $foo');


  var wf = WannabeFunction();
  var out = wf('Hello,', 'Dart', 'World');
  print('out: $out');
  
  
  SortedCollection coll = SortedCollection(sort);
  print('coll.compare is Functiuon? ${coll.compare is Function}');
  print('coll.compare is Compare? ${coll.compare is Compare}');

  print('sort2 is Compare<int>? ${sort2 is Compare2<int>}');
  */

  testLib();
}

void testLib() {
  var num1 = num.parse('0.50');
  print('num1= $num1, num1 is double: ${num1 is double}');
  var num2 = num.parse('42');
  print('num2= $num2, num2 is int: ${num2 is int}');
  var num3 = num.parse('0x42');
  print('num3= $num3, num3 is int: ${num3 is int}');

  var num4 = int.parse('42', radix: 16);
  print('num4= $num4, num4 is int: ${num4 is int}');

  print('${42.toString()}');
  print('${123.456.toString()}');
  print('${123.456.toStringAsFixed(2)}');
  print('${123.456.toStringAsPrecision(2)}');
  print('${double.parse('1.2e+2') is double}');

  print('\u{1D11E}');

  for (var char in 'Hello Dart'.split(' ')) {
    print(char);
  }

  var codeUnitList = 'Never odd or even'.codeUnits.toList();
  print('${codeUnitList[0]}');

  var s1 = 'structured web apps';
  var s2 = 'STRUCTURED WEB APPS';
  print('s1.toUpperCase= ${s1.toUpperCase()}');
  print('s2.toUpperCase= ${s2.toLowerCase()}');

  var es1 = '    hello    ';
  var es2 = '';
  var es3 = '    ';
  print('es1.trim= ${es1.trim()}');
  print('es2.isEmpty= ${es2.isEmpty}');
  print('es3.isEmpty= ${es3.isEmpty}');

  var greetingTemplate = 'Hello, NAME!';
  var greeting = greetingTemplate.replaceAll(RegExp('NAME'), 'Bob');

  // greetingTemplate 没有改变。
  print('greeting != greetingTemplate? ${greeting != greetingTemplate}');

  var sb = StringBuffer();
  sb
    ..write('Use a StringBuffer for ')
    ..writeAll(['efficient', 'string', 'creation'], ' ')
    ..write('.');

  var fullString = sb.toString();

  print('fullString= $fullString');

  // 下面正则表达式用于匹配一个或多个数字。
  var numbers = RegExp(r'\d+');

  var allCharacters = 'llamas live fifteen to twenty years';
  var someDigits = 'llamas live 15 to 20 years';

// contains() 能够使用正则表达式。
  print('allCharacters.contains(numbers)= ${allCharacters.contains(numbers)}');
  print('someDigits.contains(numbers)= ${someDigits.contains(numbers)}');

// 替换所有匹配对象为另一个字符串。
  var exedOut = someDigits.replaceAll(numbers, 'XX');
  print('exedOut= $exedOut');

  print('numbers.hasMatch(someDigits)= ${numbers.hasMatch(someDigits)}');

  for (var match in numbers.allMatches(someDigits)) {
    print('match= ${match.group(0)}');
  }

  var fruits = ['bananas', 'apples', 'oranges'];

  fruits.sort((a, b) => a.compareTo(b));
  print('fruits[0]= ${fruits[0]}');

  fruits = List<String>();

  fruits.add('apples');
  var fruit = fruits[0];
  print('fruit is String: ${fruit is String}');

  // fruits.add(5);

  testMaps();
  testUri();
  testDataTime();

  testTools();

  testImplementingMapKeys();

  testIterator();

  testMath();

  testConvert();
}

void testConvert() {
  // 提示：在 JSON 字符串中，必须使用双引号（"）,
  // 而不是单引号（'）。
  // 下面是 JSON 字符串，非 Dart 字符串。
  var jsonString = '''
  [
    {"score": 40},
    {"score": 80}
  ]
  ''';

  var scores = jsonDecode(jsonString);
  print('scores is List? ${scores is List}');

  var firstScore = scores[0];
  print('firstScore is Map? ${firstScore is Map}');
  print('firstScore[score]= ${firstScore['score']}');

  scores = [
    {'score': 40},
    {'score': 80},
    {'score': 100, 'overtime': true, 'special_guest': null}
  ];

  var jsonText = jsonEncode(scores);
  print('jsonText= $jsonText');

  print(jsonText ==
      '[{"score":40},{"score":80},'
          '{"score":100,"overtime":true,'
          '"special_guest":null}]');


  List<int> utf8Bytes = [
    0xc3, 0x8e, 0xc3, 0xb1, 0xc5, 0xa3, 0xc3, 0xa9,
    0x72, 0xc3, 0xb1, 0xc3, 0xa5, 0xc5, 0xa3, 0xc3,
    0xae, 0xc3, 0xb6, 0xc3, 0xb1, 0xc3, 0xa5, 0xc4,
    0xbc, 0xc3, 0xae, 0xc5, 0xbe, 0xc3, 0xa5, 0xc5,
    0xa3, 0xc3, 0xae, 0xe1, 0xbb, 0x9d, 0xc3, 0xb1
  ];

  var funnyWord = utf8.decode(utf8Bytes);
  print('funnyWord= $funnyWord');

  var encoded = utf8.encode('Îñţérñåţîöñåļîžåţîờñ');
  print('encode.length==utf8Bytes.length? ${encoded.length==utf8Bytes.length}');
  for(int i = 0; i < encoded.length; i++) {
    int utf8Byte = encoded[i];
    print('utf8Byte= $utf8Byte, encoded[i] == utf8Bytes[i]? ${encoded[i] == utf8Bytes[i]}');
  }
}

void testMath() {
  print('cos(pi)= ${cos(pi)}');

  var degrees = 30;
  var radians = degrees * pi / 180;
  var sin30degrees = sin(radians);
  print('sin30degrees= $sin30degrees');

  var ma = max(1, 1000);
  var mi = min(1, -1000);
  print('ma= $ma, mi= $mi');

  print('e= $e');
  print('pi= $pi');
  print('sqrt2= $sqrt2');
  print('sqrt1_2 = $sqrt1_2');

  var random = Random();
  print('random.nextDouble= ${random.nextDouble()}');
  print('random.nextInt= ${random.nextInt(10)}');
  print('random.nextBool= ${random.nextBool()}');
}

class FooException implements Exception {
  final String msg;

  FooException([this.msg]);

  @override
  String toString() => msg ?? 'FooException';
}

void testIterator() {
  for (var process in Processes()) {}
}

class Process {
  String name;
}

class ProcessIterator implements Iterator<Process> {
  @override
  Process get current => null;

  @override
  bool moveNext() {
    return null;
  }
}

class Processes extends IterableBase<Process> {
  @override
  Iterator<Process> get iterator => ProcessIterator();
}

void testImplementingMapKeys() {
  var p1 = PersonP('Yuangu', 'dashen');
  var p2 = PersonP('Yuangu', 'dashen');

  var p3 = 'not a personp';
  print('p1.hashCode == p2.hashCode: ${p1.hashCode == p2.hashCode}');
  print('p1 == p2: ${p1 == p2}');
  print('p1 == p3: ${p1 == p3}');
}

class PersonP {
  final String firstName, lastName;

  PersonP(this.firstName, this.lastName);

  @override
  bool operator ==(Object other) {
    if (other is! PersonP) return false;
    PersonP personP = other;
    return (personP.firstName == firstName && personP.lastName == lastName);
  }

  @override
  int get hashCode {
    int result = 17;
    result = result * 37 + firstName.hashCode;
    result = result * 37 + lastName.hashCode;
    return result;
  }
}

void testTools() {
  var shortLine = Line(10);
  var longLine = Line(100);
  print('shortLine.compareTo(longLine)= ${shortLine.compareTo(longLine)}');
}

class Line implements Comparable<Line> {
  final int length;

  const Line(this.length);

  @override
  int compareTo(Line other) => length - other.length;
}

void testDataTime() {
  // 获取当前时刻。
  var now = DateTime.now();
  print('now= $now');

// 更具本地时区创建 DateTime 对象。
  var y2k = DateTime(2000); // January 1, 2000
  print('y2k= $y2k');

// 指定年月日。
  y2k = DateTime(2000, 1, 2); // January 2, 2000
  print('y2k= $y2k');

// 将日期指定为 UTC 时区。
  y2k = DateTime.utc(2000); // 1/1/2000, UTC
  print('y2k= $y2k');

// 指定自Unix纪元以来，以毫秒为单位的日期和时间。
  y2k = DateTime.fromMillisecondsSinceEpoch(946684800000, isUtc: true);
  print('y2k= $y2k');

// 解析ISO 8601日期。
  y2k = DateTime.parse('2000-01-01T00:00:00Z');
  print('y2k= $y2k');

  print('y2k.millisecondsSinceEpoch= ${y2k.millisecondsSinceEpoch}');

  var unixEpoch = DateTime.utc(1970);
  print(
      'unixEpoch.millisecondsSinceEpoch= ${unixEpoch.millisecondsSinceEpoch}');

  var y2001 = y2k.add(Duration(days: 366));
  print('y2001.year= ${y2001.year}');

  var december2000 = y2001.subtract(Duration(days: 30));
  print('december2000= ${december2000.year}.${december2000.month}');

  var duration = y2001.difference(y2k);
  print('duration.inDays= ${duration.inDays}');
}

void testUri() {
  var uri = 'http://example.org/api?foo=some message';

  var encoded = Uri.encodeFull(uri);
  print('encoded= $encoded');

  var decoded = Uri.decodeFull(encoded);
  print('decoded= $decoded');

  encoded = Uri.encodeComponent(uri);
  print('encoded= $encoded');

  decoded = Uri.decodeComponent(encoded);
  print('decoded= $decoded');

  var uri1 = Uri.parse('http://example.org:8080/foo/bar#frag');

  print('uri1.scheme= ${uri1.scheme}');
  print('uri1.host= ${uri1.host}');
  print('uri1.path= ${uri1.path}');
  print('uri1.fragment= ${uri1.fragment}');
  print('uri1.origin= ${uri1.origin}');

  var uri2 = Uri(
      scheme: 'http', host: 'example.org', path: '/foo/bar', fragment: 'frag');

  print('uri2= ${uri2.toString()}');
}

void testMaps() {
  // map 通常使用字符串作为 key。
  var hawaiianBeaches = {
    'Oahu': ['Waikiki', 'Kailua', 'Waimanalo'],
    'Big Island': ['Wailea Bay', 'Pololu Beach'],
    'Kauai': ['Hanalei', 'Poipu']
  };

  // map 可以通过构造函数构建。
  var searchTerms = Map();

  // map 是参数化类型；
  // 可以指定一个 map 中 key 和 value 的类型。
  var nobleGases = Map<int, String>();

  nobleGases = {54: 'xenon'};

  // 使用 key 检索 value 。
  print('nobleGases[54] == xenon: ${nobleGases[54] == 'xenon'}');

  // 检查 map 是否包含 key 。
  print('nobleGases.containsKey(54)= ${nobleGases.containsKey(54)}');

  // 移除一个 key 及其 value。
  nobleGases.remove(54);
  print('nobleGases.containsKey(54)= ${nobleGases.containsKey(54)}');

  print(
      'hawaiianBeaches.containsKey(Oahu)= ${hawaiianBeaches.containsKey('Oahu')}');
  print(
      'hawaiianBeaches.containsKey(Florida)= ${hawaiianBeaches.containsKey('Florida')}');

  var teamAssignments = {};
  teamAssignments.putIfAbsent('Catcher', () => pickToughestKid());
  print('teamAssignments[Catcher]= ${teamAssignments['Catcher']}');

  var coffees = [];
  var teas = ['green', 'black', 'chamomile', 'earl grey'];
  print('coffees.isEmpty= ${coffees.isEmpty}');
  print('teas.isEmpty= ${teas.isEmpty}');

  teas.forEach((tea) => print('I drink $tea'));

  hawaiianBeaches.forEach((k, v) {
    print('I want to visit $k and swim at $v');
    // 我想去瓦胡岛并且在
    // [Waikiki, Kailua, Waimanalo]游泳， 等等。
  });

  var loudTeas = teas.map((tea) => tea.toUpperCase());
  loudTeas.forEach(mprint);

  loudTeas = teas.map((tea) => tea.toUpperCase()).toList();
  loudTeas.forEach(mprint);

  bool isDecaffeinated(String teaName) => teaName == 'chamomile';

// 使用 where() 来查找元素，
// 这些元素在给定的函数中返回 true 。
  var decaffeinatedTeas = teas.where((tea) => isDecaffeinated(tea));
// 或者 teas.where(isDecaffeinated)

  decaffeinatedTeas.forEach(mprint);

// 使用 any() 来检查集合中是否至少有一个元素满足条件。
  print('teas.any(isDecaffeinated)= ${teas.any(isDecaffeinated)}');

// 使用 every() 来检查集合中是否所有元素满足条件。
  print('teas.every(isDecaffeinated)= ${teas.every(isDecaffeinated)}');
}

void mprint(tea) => print('I drink $tea');

pickToughestKid() {
  return 'aaaaaaa';
}

/***********************************/
typedef Compare = int Function(Object a, Object b);

class SortedCollection {
  Compare compare;

  SortedCollection(this.compare);

  @Todo('SortedCollection', 'doSom()')
  void doSom() {}
}

int sort(Object a, Object b) => 0;
////////////////////////////////////
typedef Compare2<T> = int Function(T a, T b);

int sort2(int a, int b) => a - b;

class Point {
  num x, y;

  Point(this.x, this.y);

  Point.origin() {
    this.x = 0;
    this.y = 0;
  }

  Point.fromJson(Map<String, num> json)
      : x = json['x'],
        y = json['y'] {
    print('In Point.fromJson(): ($x, $y)');
  }

  Point.withAssert(this.x, this.y) : assert(x >= 0) {
    print('In Point.withAssert(): ($x, $y)');
  }
}

class PointP {
  final num x;
  final num y;
  final num distanceFromOrigin;

  PointP(x, y)
      : x = x,
        y = y,
        distanceFromOrigin = sqrt(x * x + y * y);
}

class PointX extends Point {
  num z;

  PointX(num x, num y, this.z) : super(x, y);

  PointX.origin() : super.origin() {
    this.z = 0;
  }
}

class Person {
  String firstName;

  Person.fromJson(Map data) {
    print('--in Person');
  }
}

class Employee extends Person {
  Employee.fromJson(Map data) : super.fromJson(data) {
    print('--in Employee');
  }
}

class Logger {
  final String name;
  bool mute = false;

  static final Map<String, Logger> _cache = <String, Logger>{};

  factory Logger(String name) {
    if (_cache.containsKey(name)) {
      return _cache[name];
    } else {
      final logger = Logger._internal(name);
      _cache[name] = logger;
      return logger;
    }
  }

  Logger._internal(this.name);

  void log(String msg) {
    if (!mute) print(msg);
  }
}

// person 类。 隐式接口里面包含了 greet() 方法声明。
class Person1 {
  // 包含在接口里，但只在当前库中可见。
  final _name;

  // 不包含在接口里，因为这是一个构造函数。
  Person1(this._name);

  // 包含在接口里。
  String greet(String who) => 'Hello, $who. I am $_name.';
}

// person 接口的实现。
class Impostor implements Person1 {
  get _name => '';

  String greet(String who) => 'Hi $who. Do you know who I am?';
}

String greetBob(Person1 person) => person.greet('Bob');

class Vector {
  final int x, y;

  Vector(this.x, this.y);

  Vector operator +(Vector v) => Vector(x + v.x, y + v.y);

  Vector operator -(Vector v) => Vector(x - v.x, y - v.y);

  @override
  bool operator ==(Object other) =>
      identical(this, other) ||
      other is Vector &&
          runtimeType == other.runtimeType &&
          x == other.x &&
          y == other.y;

  @override
  int get hashCode => x.hashCode ^ y.hashCode;
}

abstract class SomeBaseClass {}

class Foo<T extends SomeBaseClass> {
  @override
  String toString() => "Instance of 'Foo<$T>'";
}

class Extender extends SomeBaseClass {}

class WannabeFunction {
  call(String a, String b, String c) => "$a $b $c!";
}
